package cc.rengu.oltp.utility.util;

import cc.rengu.oltp.utility.bean.RedisConfig;
import cc.rengu.utility.base.PlaceholderResolver;
import cc.rengu.utility.cache.UnifiedCache;
import redis.clients.jedis.JedisPubSub;

import java.lang.reflect.Method;
import java.util.*;

/**
 * Redis服务实现类
 * Created by wangc on 2020/12/14.
 */
public class RedisUtil {
    private static final String OLTP_REDIS_CLIENT_CACHE = "OLTP_REDIS_CLIENT_CACHE";
    private static final String OLTP_REDIS_CONFIG = "OLTP_REDIS_CONFIG";
    private static final String OLTP_REDIS_CLIENT = "OLTP_REDIS_CLIENT";

    private static Object getRedisClientInstance() throws Exception {
        /* 获取Redis配置信息 */
        RedisConfig redisConfig = (RedisConfig) UnifiedCache.get(OLTP_REDIS_CLIENT_CACHE, OLTP_REDIS_CONFIG);
        if (null == redisConfig) {
            redisConfig = redisConfigInit();
            UnifiedCache.clear(OLTP_REDIS_CLIENT_CACHE);
            UnifiedCache.set(OLTP_REDIS_CLIENT_CACHE, OLTP_REDIS_CONFIG, redisConfig);
        }
        Class clasz = Class.forName(redisConfig.getClassPath().trim());
        Object redisClientInstance = clasz.newInstance();
        Method redisMethod = clasz.getMethod("redisClientInit", RedisConfig.class);
        /* 获取Redis连接池实例 */
        Object redisClient = UnifiedCache.get(OLTP_REDIS_CLIENT_CACHE, OLTP_REDIS_CLIENT);
        if (null == redisClient) {
            redisClient = redisMethod.invoke(redisClientInstance, redisConfig);
            if (null != redisClient) {
                UnifiedCache.set(OLTP_REDIS_CLIENT_CACHE, OLTP_REDIS_CLIENT, redisClient);
            } else {
                throw new Exception("redisClient init error!");
            }
        }
        return redisClientInstance;
    }

    public static boolean onceSet(String key, String value) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("set", String.class, String.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, value);
    }

    public static boolean onceSet(String key, String value, int activeTime) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("set", String.class, String.class, int.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, value, activeTime);
    }

    public static String onceGet(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("get", String.class);
        return (String) redisMethod.invoke(redisClientInstance, key);
    }

    public static String getAndSet(String key, String value) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("getAndSet", String.class, String.class);
        return (String) redisMethod.invoke(redisClientInstance, key, value);
    }

    public static long onceIncr(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("incr", String.class);
        return (long) redisMethod.invoke(redisClientInstance, key);
    }

    public static long onceDecr(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("decr", String.class);
        return (long) redisMethod.invoke(redisClientInstance, key);
    }

    public static boolean onceExists(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("exists", String.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key);
    }

    public static long hashSet(String key, String field, String value) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashSet", String.class, String.class, String.class);
        return (long) redisMethod.invoke(redisClientInstance, key, field, value);
    }

    public static long hashSet(String key, String field, String value, int activeTime) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashSet", String.class, String.class, String.class, int.class);
        return (long) redisMethod.invoke(redisClientInstance, key, field, value, activeTime);
    }

    public static String hashGet(String key, String field) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashGet", String.class, String.class);
        return (String) redisMethod.invoke(redisClientInstance, key, field);
    }

    public static boolean hashMultSet(String key, Map<String, String> filedValue) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashMultSet", String.class, Map.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, filedValue);
    }

    public static List<String> hashMultGet(String key, String... fields) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashMultGet", String.class, String[].class);
        return (List<String>) redisMethod.invoke(redisClientInstance, key, fields);
    }

    public static long getHashLen(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("getHashLen", String.class);
        return (long) redisMethod.invoke(redisClientInstance, key);
    }

    public static Map<String, String> hashGetAll(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashGetAll", String.class);
        return (Map<String, String>) redisMethod.invoke(redisClientInstance, key);
    }

    public static Set<String> hashKeys(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashKeys", String.class);
        return (Set<String>) redisMethod.invoke(redisClientInstance, key);
    }

    public static boolean hashExists(String key, String field) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashExists", String.class, String.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, field);
    }

    public static long hashDelete(String key, String... fields) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashDelete", String.class, String[].class);
        return (long) redisMethod.invoke(redisClientInstance, key, fields);
    }

    public static long hashIncrease(String key, String field, long increment) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("hashIncrease", String.class, String.class, long.class);
        return (long) redisMethod.invoke(redisClientInstance, key, field, increment);
    }

    public static long getListLen(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("getListLen", String.class);
        return (long) redisMethod.invoke(redisClientInstance, key);
    }

    public static List<String> getListRange(String key, long start, long end) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listRange", String.class, long.class, long.class);
        return (List<String>) redisMethod.invoke(redisClientInstance, key, start, end);
    }

    public static String onceListTrim(String key, long start, long end) {
        try {
            Object redisClientInstance = getRedisClientInstance();
            Method redisMethod = redisClientInstance.getClass().getMethod("listTrim", String.class, long.class, long.class);
            boolean isOk = (boolean) redisMethod.invoke(redisClientInstance, key, start, end);
            if (isOk) {
                return "OK";
            } else {
                return "FAIL";
            }
        } catch (Exception e) {
            return "FAIL:" + cc.rengu.utility.base.StringUtil.ExceptionToString(e);
        }
    }

    public static boolean listTrim(String key, long start, long end) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listTrim", String.class, long.class, long.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, start, end);
    }

    public boolean listSet(String key, long index, String value) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listSet", String.class, long.class, String.class);
        return (boolean) redisMethod.invoke(redisClientInstance, key, index, value);
    }

    public static String getListIndex(String key, long index) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listGet", String.class, long.class);
        return (String) redisMethod.invoke(redisClientInstance, key, index);
    }

    public static long onceListRem(String key, long count, String value) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listRem", String.class, long.class, String.class);
        return (long) redisMethod.invoke(redisClientInstance, key, count, value);
    }

    public static long listLeftPush(String key, String... values) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listLeftPush", String.class, String[].class);
        return (long) redisMethod.invoke(redisClientInstance, key, values);
    }

    public static long onceLeftPushX(String key, String... values) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listLeftPushX", String.class, String[].class);
        return (long) redisMethod.invoke(redisClientInstance, key, values);
    }

    public static String onceLeftPop(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listLeftPop", String.class);
        return (String) redisMethod.invoke(redisClientInstance, key);
    }

    public static long onceRightPush(String key, String... values) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listRightPush", String.class, String[].class);
        return (long) redisMethod.invoke(redisClientInstance, key, values);
    }

    public static long onceRightPushX(String key, String... values) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listRightPushX", String.class, String[].class);
        return (long) redisMethod.invoke(redisClientInstance, key, values);
    }

    public static String onceRightPop(String key) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("listRightPop", String.class);
        return (String) redisMethod.invoke(redisClientInstance, key);
    }

    public static long onceDel(String... keys) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("delete", String[].class);
        return (long) redisMethod.invoke(redisClientInstance, (Object) keys);
    }

    public static void onceExpire(String key, int activeTime) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("setKeyExpire", String.class, int.class);
        redisMethod.invoke(redisClientInstance, key, activeTime);
    }

    public static Set<String> keys(String pattern) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        try {
            Method redisMethod = redisClientInstance.getClass().getMethod("getKeys", String.class);
            return (Set<String>) redisMethod.invoke(redisClientInstance, pattern);
        } catch (Exception e) {
            Method redisMethod = redisClientInstance.getClass().getMethod("scan", String.class);
            List<String> scanKeys = (List<String>) redisMethod.invoke(redisClientInstance, pattern);
            return new HashSet<>(scanKeys);
        }
    }

    public static List<String> scan(String pattern) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("scan", String.class);
        return (List<String>) redisMethod.invoke(redisClientInstance, pattern);
    }

    public static long publish(String channel, String message) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("publish", String.class, String.class);
        return (long) redisMethod.invoke(redisClientInstance, channel, message);
    }

    public static void psubscribe(JedisPubSub subscribeListener, String channel) throws Exception {
        Object redisClientInstance = getRedisClientInstance();
        Method redisMethod = redisClientInstance.getClass().getMethod("psubscribe", JedisPubSub.class, String.class);
        redisMethod.invoke(redisClientInstance, subscribeListener, channel);
    }

    /**
     * Redis配置初始化
     *
     * @return Redis配置参数
     */
    private static RedisConfig redisConfigInit() {
        RedisConfig redisConfig = new RedisConfig();
        /* 集群部署标识 */
        String keyValue = XmlConfigUtil.getXmlConfigNodeAttrValue("redis", "classpath");
        if (!StringUtil.isEmptyOrNull(keyValue) && !StringUtil.isEmptyOrNull(keyValue.trim())) {
            keyValue = PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()));
            if (!StringUtil.isEmptyOrNull(keyValue) && !StringUtil.isEmptyOrNull(keyValue.trim())) {
                redisConfig.setClassPath(keyValue);
            }
        }
        /* 集群部署标识 */
        keyValue = XmlConfigUtil.getXmlConfigNodeAttrValue("redis", "clusterFlag");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setClusterFlag(Boolean.parseBoolean(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        if (redisConfig.isClusterFlag()) {
            /* 集群模式节点列表 */
            String clusterNodes = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "clusterNodes");
            if (!StringUtil.isEmptyOrNull(clusterNodes)) {
                redisConfig.setClusterNodes(PlaceholderResolver.resolve(clusterNodes.trim(), Collections.singletonList(PlaceholderResolver.getDefault())));
            }
            /* 集群工作模式 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "clusterMode");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setClusterMode(PlaceholderResolver.resolve(keyValue.trim().toLowerCase(), Collections.singletonList(PlaceholderResolver.getDefault())));
            }
            /* 哨兵模式主节点 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "masterName");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setMasterName(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault())));
            }
            /* 最大重定向数目 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "maxRedirects");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setMaxRedirects(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
            }
            /* 重连尝试次数 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "maxAttempts");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setMaxAttempts(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
            }
        } else {
            /* 单机模式主机地址 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "host");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setHost(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault())));
            }
            /* 单机模式主机端口 */
            keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "port");
            if (!StringUtil.isEmptyOrNull(keyValue)) {
                redisConfig.setPort(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
            }
        }
        /* 强制刷新标识 */
        keyValue = XmlConfigUtil.getXmlConfigNodeAttrValue("redis", "forceRefleshFlag");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setForceRefleshFlag(Boolean.parseBoolean(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 最大分配的对象数 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "maxTotal");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setMaxTotal(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 最大能够保持idle状态的对象数 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "maxIdle");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setMaxIdle(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 最大等待时间 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "maxWaitMillis");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setMaxWaitMillis(Long.parseLong(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 在获取连接时有效性检查标志 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "testOnBorrow");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setTestOnBorrow(Boolean.parseBoolean(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 释放连接到连接池时有效性检查标志 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "testOnReturn");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setTestOnReturn(Boolean.parseBoolean(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 空闲时连接有效性检查标志 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "testWhileIdle");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setTestWhileIdle(Boolean.parseBoolean(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 超时时间 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "timeOut");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setTimeout(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        /* 密码 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "password");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setPassword(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault())));
        }
        /* 数据库编号 */
        keyValue = XmlConfigUtil.getXmlConfigCacheKeyValue("redis", "database");
        if (!StringUtil.isEmptyOrNull(keyValue)) {
            redisConfig.setDataBaseNo(Integer.parseInt(PlaceholderResolver.resolve(keyValue.trim(), Collections.singletonList(PlaceholderResolver.getDefault()))));
        }
        return redisConfig;
    }
}
